#ifndef xpack_utils_tuple
#define xpack_utils_tuple
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::utils_tuple::inc
#include"define/nullref.hpp"
#include"macro/xexport.hpp"
#include"macro/xforward.hpp"
#include"macro/xstruct.hpp"
#include"macro/xnew.hpp"
#include"macro/xmaybe_unused.hpp"
#include"macro/xthread_local.hpp"
#include"meta/function.hpp"
#include"meta/has_constructor.hpp"
#include"meta/has_invoke.hpp"
#include"meta/invoke_type.hpp"
#include"meta/is_origin_array.hpp"
#include"meta/is_origin_string.hpp"
#include"meta/item_origin_of.hpp"
#include"meta/remove_ref.hpp"
#include"utils/static_length_of.hpp"
#pragma pop_macro("xuser")

namespace mixc::utils_tuple{
    template<uxx i_v, class item_t>
    xstruct(
        xtmpl(tuple_item, i_v, item_t),
        xpubf_no_mut(m_value, item_t)
    )
        template<class ... args_t>
        requires(sizeof...(args_t) == 0)
        constexpr tuple_item(xmaybe_unused args_t const & ... dummy) : m_value() {}
        constexpr tuple_item(item_t & value) : m_value(value){}
        constexpr tuple_item(item_t && value) : m_value((item_t &&)value){}

        auto & get() const {
            return m_value;
        }

        auto & get() {
            return m_value;
        }
    $

    template<uxx i_v, inc::is_origin_array array_t>
    requires(inc::is_origin_string<array_t> == false)
    xstruct(
        xspec(tuple_item, i_v, array_t),
        xpubf_no_mut(m_value, char[sizeof(array_t)])
    )
        using item_t    = inc::item_origin_of<array_t>;
        using itemp_t   = item_t *;

        template<class ... args_t>
        requires(sizeof...(args_t) == 0)
        constexpr tuple_item(xmaybe_unused args_t const & ... dummy) {
            xnew(m_value) array_t();
        }
        constexpr tuple_item(array_t & value){
            for(uxx i = 0; i < inc::static_length_of(array_t{}); i++){
                xnew(itemp_t(m_value) + i) itemp_t(value[i]);
            }
        }
        constexpr tuple_item(array_t && value){
            for(uxx i = 0; i < inc::static_length_of(array_t{}); i++){
                xnew(itemp_t(m_value) + i) itemp_t((item_t &&)value[i]);
            }
        }

        auto & get() const {
            return *(array_t *)m_value;
        }

        auto & get() {
            return *(array_t *)m_value;
        }
    $

    template<uxx i_v, inc::is_origin_string cstr_t>
    xstruct(
        xspec(tuple_item, i_v, cstr_t),
        xpubf_no_mut(m_value, inc::item_origin_of<cstr_t> const *)
    )
        template<class ... args_t>
        requires(sizeof...(args_t) == 0)
        constexpr tuple_item(xmaybe_unused args_t const & ... dummy) : m_value() {}
        constexpr tuple_item(cstr_t & value) : m_value(value){}
        constexpr tuple_item(cstr_t && value) : m_value(value){}

        auto & get() const {
            return m_value;
        }

        auto & get() {
            return m_value;
        }
    $

    struct tuple_info{
        uxx     index               : xbitsof(uxx) - 1;
        uxx     is_last_argument    : 1;
    };

    template<class lambda_t, class item_t>
    concept can_foreach = requires(lambda_t lambda, item_t value){
        lambda(value);
    };

    template<class lambda_t, class item_t>
    concept can_foreach_with_loop_control = requires(lambda_t lambda, item_t value, loop_t state){
        state = lambda(value);
    };

    template<class lambda_t, class item_t>
    concept can_foreach_with_info = requires(lambda_t lambda, item_t value, tuple_info info){
        lambda(value, info);
    };

    template<class lambda_t, class item_t>
    concept can_foreach_with_info_and_loop_control = requires(lambda_t lambda, item_t value, loop_t state, tuple_info info){
        state = lambda(value, info);
    };

    xthread_local(l_index, uxx);
}

namespace mixc::utils_tuple::origin{
    using ::mixc::utils_tuple::tuple_info;

    template<class a0_t = decltype(nullptr), class ... args_t>
    xstruct(
        xtmpl(tuple, a0_t, args_t...),
        xprob(tuple_item<1 + sizeof...(args_t), a0_t>),
        xprob(tuple<args_t...>)
    )
        using base_item_t   = tuple_item<1 + sizeof...(args_t), a0_t>;
        using base_t        = tuple<args_t...>;

        template<class ...>
        constexpr tuple(){}

    #if xfor_vscode_hint
        constexpr tuple(a0_t first, args_t ... rest) : 
            base_item_t((a0_t &&)first), base_t(((args_t &&)rest)...){
            // 支持隐式推导
        }
    #else
        constexpr tuple(a0_t & first, args_t const & ... rest) : 
            base_item_t((a0_t &)first), base_t(((args_t &&)rest)...){
            // 支持隐式推导
        }

        constexpr tuple(a0_t && first, args_t const & ... rest) : 
            base_item_t((a0_t &&)(first)), base_t(((args_t &&)rest)...){
            // 支持隐式推导
        }
        
    #endif

        template<class a0x_t, class ... argsx_t>
        requires(
            inc::has_constructor<base_item_t, void(a0x_t &&)> and 
            inc::has_constructor<base_t, void(argsx_t && ...)>
        )
        constexpr tuple(a0x_t && first, argsx_t && ... rest) : 
            base_item_t((a0x_t &&)(first)), base_t(((argsx_t &&)rest)...){
        }

        template<class a0x_t, class ... argsx_t>
        requires(
            inc::has_constructor<a0_t, void(a0x_t)> and ... and 
            inc::has_constructor<args_t, void(argsx_t)>
        )
        constexpr tuple(tuple<a0x_t, argsx_t...> && other) : 
            base_item_t((a0x_t &&)other.template get<0>()), base_t((tuple<argsx_t...> &&)other){
        }

        template<class a0x_t, class ... argsx_t>
        requires(
            inc::has_constructor<a0_t, void(a0x_t)> and ... and 
            inc::has_constructor<args_t, void(argsx_t)>
        )
        constexpr tuple(tuple<a0x_t, argsx_t...> & other) : 
            base_item_t((a0x_t &)other.template get<0>()), base_t((tuple<argsx_t...> &)other){
        }

        static constexpr uxx length(){
            return 1 + sizeof...(args_t);
        }

        template<ixx index_v, ixx adjust_index_v = index_v >= 0 ? index_v : the_t::length() + index_v>
        requires(adjust_index_v < the_t::length())
        auto & get() const {
            if constexpr (adjust_index_v == 0){
                return base_item_t::get();
            }
            else{
                return base_t::template get<adjust_index_v - 1>();
            }
        }

        template<ixx index_v, class val_t, ixx adjust_index_v = index_v >= 0 ? index_v : the_t::length() + index_v>
        requires(adjust_index_v < the_t::length())
        auto & set(val_t && value){
            get<index_v>() = xforward(value);
            return this[0];
        }

        template<class lambda_t>
        requires(can_foreach<lambda_t, a0_t> and ... and can_foreach<lambda_t, args_t>)
        void foreach(lambda_t const & lambda){
            if constexpr (can_foreach_with_loop_control<lambda_t, a0_t> == false){
                lambda(base_item_t::get());
                base_t::foreach(lambda);
            }
            else if (lambda(base_item_t::get()) != loop_t::finish){
                base_t::foreach(lambda);
            }
        }

        template<class lambda_t>
        requires(can_foreach_with_info<lambda_t, a0_t> and ... and can_foreach_with_info<lambda_t, args_t>)
        void foreach(lambda_t const & lambda){
            tuple_info info{ .index = (*l_index)++, .is_last_argument = sizeof...(args_t) == 0 };

            if constexpr (can_foreach_with_info_and_loop_control<lambda_t, a0_t> == false){
                lambda(base_item_t::get(), info);
                base_t::foreach(lambda);
            }
            else if (lambda(base_item_t::get(), info) != loop_t::finish){
                base_t::foreach(lambda);
            }
            
            *l_index = 0;
        }

        /**
         * @brief 
         * 将当前 tuple 所有元素作为参数传入 callback 中
         * @tparam return_t callback 返回值类型
         * @tparam func_t 回调函数
         */
        template<class return_t, class func_t>
        requires(inc::has_invoke<func_t, return_t(*)(a0_t, args_t...)>)
        return_t flatten_param_invoke(func_t && callback){
            return flatten_param_invoke_core(xforward(callback));
        }

        /**
         * @brief 
         * 将当前 tuple 所有元素作为参数传入 callback 中
         * @tparam func_t 回调函数
         */
        template<
            class func_t,
            class invoke_t = inc::invoke_type<func_t>,
            class return_t = typename inc::function<invoke_t>::return_type
        >
        requires(inc::has_invoke<func_t, return_t(*)(a0_t, args_t...)>)
        return_t flatten_param_invoke(func_t && callback){
            return flatten_param_invoke_core(xforward(callback));
        }

    protected:
        auto flatten_param_invoke_core(auto && callback, auto & ... args){
            auto & value = base_item_t::get();
            return base_t::flatten_param_invoke_core(xforward(callback), args..., value);
        }
    $

    template<>
    xstruct(
        xspec(tuple, decltype(nullptr))
    )
        constexpr tuple(){}
        constexpr tuple(tuple<> const &){}

        template<ixx index>
        auto & get() const {
            return inc::nullref;
        }

        template<class lambda_t>
        void foreach(lambda_t const &){}

        /**
         * @brief 
         * 将当前 tuple 所有元素作为参数传入 callback 中
         * @tparam return_t callback 返回值类型
         * @tparam func_t 回调函数
         */
        template<class return_t, class func_t>
        requires(inc::has_invoke<func_t, return_t(*)()>)
        return_t flatten_param_invoke(func_t && callback){
            return flatten_param_invoke_core(xforward(callback));
        }

        /**
         * @brief 
         * 将当前 tuple 所有元素作为参数传入 callback 中
         * @tparam func_t 回调函数
         */
        template<
            class func_t,
            class invoke_t = inc::invoke_type<func_t>,
            class return_t = typename inc::function<invoke_t>::return_type
        >
        requires(inc::has_invoke<func_t, return_t(*)()>)
        return_t flatten_param_invoke(func_t && callback){
            return flatten_param_invoke_core(xforward(callback));
        }

    protected:
        auto flatten_param_invoke_core(auto && callback, auto & ... args){
            return callback(args...);
        }
    $

    template<class ... args_t>
    requires(sizeof...(args_t) > 0)
    inline tuple<inc::remove_ref<args_t>...> make_tuple(args_t && ... list){
        return tuple<inc::remove_ref<args_t>...>(xforward(list)...);
    }
}

#endif

xexport_space(mixc::utils_tuple::origin)